/*
 ********************************************************************************
 *                      桂林聚联科技有限公司
 *                           Copyright
 *  文件描述 ： 
 *
 *
 *  文件名称 ： OtdrInterfaceAsync.c
 *  创建者   ： 
 *  创建日期 ： 2016-10-25 17:01:44
 *  当前版本 ： 
 ********************************************************************************
*/
#include <stdio.h>
#include <assert.h>
#include <math.h>

#include "prototypes.h"
#include "OtdrSpiData.h"
#include "otdrtestinterface.h"
#include "proginfo.h"

#define OtdrSleepTime   15000

/******************************** some function declare ************************************/
int wait_for_fpga_data(DeviceInfo_t *device, int waittime_ms, int (*stop)(void));
int get_one_piece_of_data_async(DeviceInfo_t *device, OtdrSpiData_t *osd, OtdrStateCtrl_t *StateCtrl);

/*
 **************************************************************************************************
 *  函数  名： startOtdrTest
 *  函数描述： 异步复用FPGA测试等待时间
 *  入口参数： 
 *  返回参数： 
 *  日期版本： 2016年 12月 26日 星期一 16:27:37 CST
 **************************************************************************************************
*/
int startOtdrTest(OtdrTestParam_t *tp, \
            ConcatParam_t *cp, \
            int (*ResultCopyBack)(OtdrTestResult_t *, OtdrTestResult_t *, int), \
            OtdrTestResult_t *OtdrTestResult, \
            int (*stop)(void), \
            DeviceInfo_t *device)
{
    extern int CheckIfGetConcat(OtdrTestParam_t *tp, ConcatParam_t *cp);
    char  timebuf[32];
	int   fscount, FpgaStartCount, RefreshCount, err_no, datamask;
    int   try, trytimes = 3;
    int   rcv, power, apdv, slice_ms;
	int   PeriodTime, TotalMeasureTime, TotalMeasureTimeBack;
	int   LowPowerTime, HighPowerTime, lastLowPowerTime, lastHighPowerTime;
	int   LowPowerTimeBack, HighPowerTimeBack;

    OtdrSpiData_t   osd;
    OtdrData_t      OtdrData, OtdrDataBack;
    FpgaStartParam_t fsp;

    OtdrTestResult_t result;
    OtdrStateCtrl_t OtdrStateCtrl;
    OtdrTestParam_t autoTP, *TestParam = (OtdrTestParam_t *)tp;
    ConcatParam_t ConcatParam;
	
    SaveRunningData_t srd;
	
    err_no = E_CMD_OK;
    printf("StartOtdrTest at time: %s\n", CurrentDateTime(timebuf));
    parsePrint();
    configOtdrAlgo("/opt/TR600plus/config/otdralgo_config", tp->DataNum);
    parseSRDconf  ("/mnt/udisk/TR600plus/config/saverunningdata.conf", &srd);
/******************************* Check OTDR Mode   *****************************************/
    if((OTDR_MODE_AVG == TestParam->OtdrMode) || (OTDR_MODE_REALTIME == TestParam->OtdrMode))
    {
        OtdrUpdateParam(&OtdrStateCtrl, TestParam);
    }
    else
    {
        m1_error("\nUnknow OtdrMode!!!!!!\n");
        err_no = E_TEST_PARAM;
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }

    if(CheckRunParamValid(&OtdrStateCtrl) == 0){
        m1_error("CheckRunParamValid failed, do NOTHING, returns\n");
        err_no = E_TEST_PARAM;
        ResultCopyBack(NULL, NULL, err_no);
        PrintOtdrStateCtrl(&OtdrStateCtrl);
        return err_no;
    }
/**************************** Check AutoMeasure param ******************************/
    err_no = adaptTestParam(tp, &autoTP, stop, device);
    if(err_no != E_CMD_OK){
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }
    else{
        OtdrUpdateParam(&OtdrStateCtrl, &autoTP);
        // copy automeasure parameters to host
        CopyAutoMeasureParam(&result, &autoTP);
        if(tp->MeasureLength_m == 0)    ResultCopyBack(OtdrTestResult, &result, err_no);
    }
    m1_print("startOtdrTest : %dnm-%dkm-%dns-%ds\n", \
            autoTP.Lambda_nm, \
            autoTP.MeasureLength_m/1000, \
            autoTP.PulseWidth_ns, \
            autoTP.MeasureTime_ms/1000);
/**************************** Check the concat param *******************************/
    if(cp == NULL){
        err_no = getConcat(tp, &ConcatParam, stop, device);
        if(err_no != E_CMD_OK){
            ResultCopyBack(NULL, NULL, err_no);
            return err_no;
        }
        memcpy(&OtdrStateCtrl.ConcatParam, &ConcatParam, sizeof(ConcatParam));
    }
    else    memcpy(&OtdrStateCtrl.ConcatParam, cp, sizeof(ConcatParam));

/********************************************************************************/
/******************** Parameters OK, StartTest **********************************/
/********************************************************************************/
    // memory alloc for SpiData and OtdrData
    err_no = SpiDataMalloc(&osd, OtdrStateCtrl.DataNum);
    err_no = OtdrDataMalloc(&OtdrData, OtdrStateCtrl.DataNum);
    err_no = OtdrDataMalloc(&OtdrDataBack, OtdrStateCtrl.DataNum);
    err_no = OtdrTestResultMalloc(&result, OtdrStateCtrl.DataNum);
    if(err_no == E_MEM_ALLOC){
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }
/***************************** Starts to Measure ***********************************/
    OtdrStateCtrl.RefreshCount = 0;
    SpiClrData(&osd);
    OtdrClrData(&OtdrData);

    // Starts fpga to test every slice_ms
    slice_ms = OtdrStateCtrl.slice_ms;
    if(OtdrStateCtrl.ConcatParam.PowerMode == POWER_MODE_CONCAT){
        if(OtdrStateCtrl.OtdrMode == OTDR_MODE_REALTIME)    slice_ms = OtdrStateCtrl.RefreshPeriod_ms/2;//500;
    }
    if(OtdrStateCtrl.OtdrMode == OTDR_MODE_AVG){
        FpgaStartCount = OtdrStateCtrl.MeasureParam.MeasureTime_ms / slice_ms;
        RefreshCount   = OtdrStateCtrl.MeasureParam.MeasureTime_ms / OtdrStateCtrl.RefreshPeriod_ms - 1;
    }
    else if(OtdrStateCtrl.OtdrMode == OTDR_MODE_REALTIME){
        FpgaStartCount = 0x7fffffff;  // in realtime mode, let it be the largest integer
        RefreshCount   = 0x7fffffff;  // in realtime mode, let it be the largest integer
    }
    else{
        FpgaStartCount = 0;           // otherwise, let it be 0
        RefreshCount   = 0;           // otherwise, let it be 0
    }

    PeriodTime        = 0;  // measure time of this period
    TotalMeasureTime  = 0;  // total measure time of this measure
    TotalMeasureTimeBack = 0;  // total measure time of this measure
    LowPowerTime      = 0;
    LowPowerTimeBack  = 0;
    HighPowerTime     = 0;
    HighPowerTimeBack = 0;
    lastLowPowerTime  = LowPowerTime;
    lastHighPowerTime = HighPowerTime;

    // start test in async mode, mutiplex using fpga wait time
    apdv = getApdV(&OtdrStateCtrl);
/****************************** StartFPGA to get data *********************************/
    GetReceiverAndPowerLevel(&OtdrStateCtrl, TotalMeasureTime, &rcv, &power);
    TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, power, rcv, apdv, slice_ms);
    fscount = 0;
    FpgaStart(device, &fsp);
    try = 0;
    do{
        err_no = wait_for_fpga_data(device, fsp.measuretime, stop);
        if(err_no == E_CMD_OK){
            err_no = get_one_piece_of_data_async(device, &osd, &OtdrStateCtrl);
            if(err_no == E_CMD_OK){
                try = 0;
                fscount++;

                PeriodTime       += slice_ms;
                TotalMeasureTime += slice_ms;

                if(OtdrStateCtrl.TreatAsHighPowerData)  HighPowerTime += slice_ms;
                else                                    LowPowerTime  += slice_ms;
            }
            else{
                try++;
                if(try >= trytimes){
                    m1_error("get_one_piece_of_data_async failed AFTER %d tries\n", try);
                    break;
                }
                m1_warning("get_one_piece_of_data_async failed, try again!\n");
            }

/******************************* start fpga again *************************************/
            if(fscount < FpgaStartCount){
                GetReceiverAndPowerLevel(&OtdrStateCtrl, TotalMeasureTime, &rcv, &power);
                TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, power, rcv, apdv, slice_ms);
                FpgaStart(device, &fsp);
                //fscount++;
                m1_print("FpgaStartCount = %d\n", fscount);
            }
            else break;
/****************************** process data if refresh *******************************/
            if((PeriodTime >= OtdrStateCtrl.RefreshPeriod_ms) && (RefreshCount > 0))
            {
                PeriodTime                     = 0;
                OtdrStateCtrl.TotalMeasureTime = TotalMeasureTime;
                OtdrStateCtrl.LowPowerTime     = LowPowerTime;
                OtdrStateCtrl.HighPowerTime    = HighPowerTime;

                datamask = 0;
                if(LowPowerTime > lastLowPowerTime){
                    OtdrStateCtrl.LowPowerDataChanged = 1;
                    datamask |= SPIDATA_MASK_LP;
                }
                else    OtdrStateCtrl.LowPowerDataChanged = 0;

                if(HighPowerTime > lastHighPowerTime)       datamask |= SPIDATA_MASK_HP;

                SpiCopyData(&OtdrData, &osd, datamask);
                OtdrStateCtrl.FindEvent = 0;
                m1_print("Starts ProcessRefreshData at time: %s\n", CurrentDateTime(timebuf));
                if(OtdrStateCtrl.OtdrMode == OTDR_MODE_REALTIME){
                    TotalMeasureTimeBack = TotalMeasureTime;
                    HighPowerTimeBack = HighPowerTime;
                    LowPowerTimeBack = LowPowerTime;
                    OtdrDataCopy(&OtdrDataBack, &OtdrData);
                }
                err_no = ProcessRefreshData(&OtdrData, &OtdrStateCtrl, &result);
                ResultCopyBack(OtdrTestResult, &result, err_no);
                RefreshCount--;
                m1_print("RefreshCount = %d\n", RefreshCount);

                if(OtdrStateCtrl.OtdrMode == OTDR_MODE_REALTIME){
                    LowPowerTime = 0;
                    HighPowerTime = 0;
                    TotalMeasureTime = 0;
                }
                lastLowPowerTime     = LowPowerTime;
                lastHighPowerTime    = HighPowerTime;
            }
        }
        else    break;  // stopped
    }while(fscount <= FpgaStartCount);
/**************************** data ready, final process ****************************/
    OtdrStateCtrl.TotalMeasureTime    = TotalMeasureTime;
    OtdrStateCtrl.LowPowerTime        = LowPowerTime;
    OtdrStateCtrl.HighPowerTime       = HighPowerTime;
    if((err_no == E_CMD_OK) || (err_no == E_STOPPED)){
        if(TotalMeasureTime > 0){     // 有数据才处理，否则返回被终止无数据
            datamask = 0;
            if(LowPowerTime > lastLowPowerTime){
                OtdrStateCtrl.LowPowerDataChanged = 1;
                datamask |= SPIDATA_MASK_LP;
            }
            else    OtdrStateCtrl.LowPowerDataChanged = 0;

            if(HighPowerTime > lastHighPowerTime)       datamask |= SPIDATA_MASK_HP;
            SpiCopyData(&OtdrData, &osd, datamask);

            OtdrStateCtrl.FindEvent = 1;
            m1_print("StartTest finished at time: %s\n", CurrentDateTime(timebuf));
            err_no = ProcessFinalData(&OtdrData, &OtdrStateCtrl, &result, &srd);
            ResultCopyBack(OtdrTestResult, &result, err_no);
        }
        else if(TotalMeasureTimeBack > 0 && OtdrStateCtrl.OtdrMode == OTDR_MODE_REALTIME){
            OtdrStateCtrl.LowPowerDataChanged = 1;
            OtdrStateCtrl.TotalMeasureTime    = TotalMeasureTimeBack;
            OtdrStateCtrl.LowPowerTime        = LowPowerTimeBack;
            OtdrStateCtrl.HighPowerTime       = HighPowerTimeBack;
            OtdrDataCopy(&OtdrData, &OtdrDataBack);
            OtdrStateCtrl.FindEvent = 1;
            m1_print("StartTest finished at time: %s\n", CurrentDateTime(timebuf));
            err_no = ProcessFinalData(&OtdrData, &OtdrStateCtrl, &result, &srd);
            ResultCopyBack(OtdrTestResult, &result, err_no);
        }
        else    ResultCopyBack(NULL, NULL, E_STOPPED);
    }
    else    ResultCopyBack(NULL, NULL, err_no);    // 其他错误，直接返回

    OtdrStateCtrl.OtdrMode = OTDR_MODE_IDLE;

    // free data
    SpiDataFree(&osd);
    OtdrDataFree(&OtdrData);
    OtdrDataFree(&OtdrDataBack);
    OtdrTestResultFree(&result);
    writeSRD(&srd);
    return err_no;
}

/*
 **************************************************************************************************
 *  函数  名： StartTestAsync
 *  函数描述： 异步复用FPGA测试等待时间
 *  入口参数： 
 *  返回参数： 
 *  日期版本： 2016年 11月 04日 星期五 11:27:44 CST
 **************************************************************************************************
*/
int startTest(OtdrTestParam_t *tp, \
            int (*ResultCopyBack)(OtdrTestResult_t *, OtdrTestResult_t *, int), \
            OtdrTestResult_t *OtdrTestResult, \
            int (*stop)(void), \
            DeviceInfo_t *device)
{
    m1_print("startTest : %dnm-%dkm-%dns-%ds\n", \
            tp->Lambda_nm, \
            tp->MeasureLength_m/1000, \
            tp->PulseWidth_ns, \
            tp->MeasureTime_ms/1000);
    return startOtdrTest(tp, NULL, ResultCopyBack, OtdrTestResult, stop, device);
}

/*
 ********************************************************************************
 *  函数名称： get_one_piece_of_data
 *  函数描述： get data from fpga, under specified parameters
 *  入口参数： 
 *  返回参数： E_CMD_OK, E_STOPPED, E_FPGA_ERR
 *  日期版本： 2016-10-21 10:57:28
 ********************************************************************************
*/
int wait_for_fpga_data(DeviceInfo_t *device, int waittime_ms, int (*stop)(void))
{
    int wt, err_no = E_CMD_OK, to = 1, sleeptime = 20;
#if DATA_SPI
    for(wt = 0; wt < 2*waittime_ms; wt += sleeptime)
    {
        if(CheckDataAvailable(device)){
            to = 0;
            break;
        }
        usleep(sleeptime*1000);
        if(stop != NULL){
            if(stop()){
                FpgaStop(device);
                err_no = E_STOPPED;
                to = -1;
                break;
            }
        }
    }
    if(to == 1){
        err_no = E_FPGA_ERR;   
        m1_error("FPGA timeout, return E_FPGA_ERR\n");
    }
#else   // DATA_FILE
    for(wt = 0; wt < waittime_ms; wt += sleeptime)
    {
        usleep(sleeptime*1000);
        if(stop != NULL){
            if(stop()){
                FpgaStop(device);
                err_no = E_STOPPED;
                break;
            }
        }
    }
#endif
    return err_no;
}

int get_one_piece_of_data_async(DeviceInfo_t *device, OtdrSpiData_t *osd, OtdrStateCtrl_t *StateCtrl)
{
    int err_no, datamask = SPIDATA_MASK_HP, add = 1;
/******************************* copy data from FPGA to spidata ****************************************/
    if(StateCtrl->TreatAsHighPowerData == 0)    datamask = SPIDATA_MASK_LP;
    if(StateCtrl->OtdrMode != OTDR_MODE_AVG)    add = 0;
    err_no = SpiGetDataFromFpga(device, osd, datamask, add);
    return err_no;
}

/*
 ********************************************************************************
 *  函数名称 ： parseSRDconf        isSRDcode
 *  函数描述 ： 解析运行时保存数据的配置文件
 *  入口参数 ： 
 *  返回参数 ： 
 *  日期版本 ： 2016-11-14 09:22:31
 ********************************************************************************
*/
void parseSRDconf(char *conf_file_path, SaveRunningData_t *srd)
{
    int code, i = 0;
    char line[32];
    FILE *fp;

    memset(srd, 0, sizeof(SaveRunningData_t));

    //fp = fopen(conf_file_path, "r");
    fp = fopen("/mnt/udisk/TR600plus/config/saverunningdata.conf", "r");
    if(NULL == fp){
        //m1_print("parseSRDconf file NOT exist\n");
        return;
    }

    while(fgets(line, 32, fp)){
        if((line[0] >= '0') && (line[0] <= '9')){
            code = strtol(line, NULL, 10);
            srd->requestCode[i++] = code;
            if(i >= 20) break;
        }
    }
    srd->requestNum = i;
    strncpy(srd->path, "/mnt/udisk/TR600plus/config/savedata/", 128);
    //m1_print("srd->requestCode = ");
    //for(i = 0; i < srd->requestNum; i++){
    //    m1_print("%d  ", srd->requestCode[i]);
    //}
    //m1_print("\n");

    fclose(fp);
}

int isSRDcode(SaveRunningData_t *srd, int code)
{
    int i, yes = 0;
    for(i = 0; i < srd->requestNum; i++){
        if(code == srd->requestCode[i]){
            yes = 1;
            break;
        }
    }
    return yes;
}

int putSRD(SaveRunningData_t *srd, char *fn, char *param, int fc, void *data, int datanum)
{
    int elemsize, n;

    n = srd->savefilenum;
    if(fc == OTDR_LOG){
        elemsize = sizeof(float);
    }
    else{
        elemsize = sizeof(int);
    }
    srd->filelist[n].data = malloc(datanum * elemsize);
    if(srd->filelist[n].data == NULL){
        m1_error("putSRD malloc failed\n");
        return E_MEM_ALLOC;
    }

    strcpy(srd->filelist[n].filename, fn);
    if(NULL != param)   strcpy(srd->filelist[n].param, param);

    srd->filelist[n].filecode = fc;
    srd->filelist[n].datanum = datanum;
    memcpy(srd->filelist[n].data, data, datanum*elemsize);
    srd->savefilenum++;
    //m1_print("putingSRD code = %d\n", fc);
    return E_CMD_OK;
}

int writeSRD(SaveRunningData_t *srd)
{
    FILE *fp;
    int *data_int;
    float *data_float;
    char *fn, filepath[256];
    int i, n, dn, fc, en = E_CMD_OK;

    for(i = 0; i < srd->savefilenum; i++){
        fn = srd->filelist[i].filename;
        fc = srd->filelist[i].filecode;
        dn = srd->filelist[i].datanum;
        data_int = (int*)srd->filelist[i].data;
        data_float = (float*)srd->filelist[i].data;

        strcpy(filepath, srd->path);
        strcat(filepath, fn);
        fp = fopen(filepath, "w");
        if(fp == NULL){
            m1_error("writeSRD open %s failed\n", fn);
            en = E_MEM_ALLOC;
            break;
        }

        // 只有对数曲线数据是浮点
        if(fc == OTDR_LOG){
            for(n = 0; n < dn; n++){
                fprintf(fp, "%.4f\n", data_float[n]);
            }
        }
        else{
            for(n = 0; n < dn; n++){
                fprintf(fp, "%d\n", data_int[n]);
            }
        }

        free((void*)data_int);
        fclose(fp);
    }

    // free
    for(; i < srd->savefilenum; i++){
        free(srd->filelist[i].data);
    }
    if(srd->savefilenum > 0)    m1_print("\nSuccessfully save %d datafiles to %s\n\n", srd->savefilenum, srd->path);
    return en;
}

/*
 ********************************************************************************
 *  函数名称 ： getLenByIndex  getIndexByLen  getDisplayNumber
 *  函数描述 ： 
 *  入口参数 ： 
 *  返回参数 ： 
 *  日期版本 ： 2016-11-25 15:01:35
 ********************************************************************************
*/
float getLenByIndex(unsigned int index, unsigned int frequency,float refraction)
{
    float mpp = C / (2*refraction*frequency);
    return index * mpp;
}

int getIndexByLen(float len, unsigned int frequency,float refraction)
{
    float ppm = 2*refraction*frequency / C;
    return (int)(len * ppm);
}

int getDisplayNumber(int MeasureLength_m , float refraction, int *displayNum)
{
    int n, frequency;
    int i = GetMeasureLengthIndex(MeasureLength_m);
    if(i == -1)     return -1;

    frequency = OtdrSampleRateHz[i];
    n = getIndexByLen((float)MeasureLength_m, frequency, refraction);

    // align to even
    if(n & 1)   n++;
    *displayNum = n;
    return 0;
}

int getFrequency(int MeasureLength_m, unsigned int * frequency)
{
    int i = GetMeasureLengthIndex(MeasureLength_m);
    if(i == -1)     return -1;
    
    *frequency = OtdrSampleRateHz[i];
    return 0;
}

int getPulseWidth_M(int MeasureLength_m, int PulseWidth_ns)
{
    unsigned int freq;
    int M = 0;

    if(getFrequency(MeasureLength_m, &freq) == 0){
        M = (int)(PulseWidth_ns * (freq / 1000) / 1e6);
        M = MAX(M, 1);
    }

    return M;
}

int getPulseWidthRange(int MeasureLength_m, int *minPW, int *maxPW)
{
    int i = GetMeasureLengthIndex(MeasureLength_m);
    if(i == -1){
        *minPW = 40;
        *maxPW = 40;
        return -1;
    }
    else{
        *minPW = OtdrMinAllowPulseWidth[i];
        *maxPW = OtdrMaxAllowPulseWidth[i];
        return 0;
    }
}

#if 0
MODULE_INFO(version, "123");
MODULE_AUTHOR("pheigenbaum@163.com");
MODULE_DESCRIPTION("OTDR算法模块，提供独立测试接口，供外部调用");
//MODULE_DEVICE_TABLE(type,name);
//MODULE_VERSION(_version);
//MODULE_FIRMWARE(_firmware);
MODULE_BUILD(__DATE__ " " __TIME__);
MODULE_DEPEND("libiniparser.so, libfpga-2016.so, libotdr-peripheral");
MODULE_PROJECT("tr600plus-algorithm");
#endif

/*
 **************************************************************************************
 *    End    of    File
 **************************************************************************************
*/
